package de.herberlin.server.common;

import java.io.OutputStream;
import java.net.*;
import java.util.logging.Logger;

import de.herberlin.server.common.event.*;

/**
 * Abstract server implementation.
 * Subclasses may start different worker threads.
 *
 * Server fires events to de.herberlin.server.event.EventDispatcher,
 * add listeners there.
 *
 * @author Hans Joachim Herbertz
 * @created 27.01.2003
 */
public abstract class AbstractServer implements Runnable {

	/**
	 * Servers state.
	 */
	private boolean isRunning = false;


	/**
	 * The port the server actually is running on
	 */
	private int currentPort=-1;
	/**
	 * Returns true if the server is listening to
	 * the given port, means Listener Thread is running.
	 */
	public boolean isAlive() {
		if (serverThread==null){
			return false;
		} else {
			return serverThread.isAlive();
		}
	}

	private Thread serverThread=null;
	protected Logger logger=Logger.getLogger(getClass().getName());


	/**
	 * Starts the server; if the server is already running does nothing.
	 *
	 */
	public void start() {
		if (isAlive())
			return;
		currentPort=getPort();
		isRunning = true;
		serverThread = new Thread(this);
		serverThread.start();
		Thread.yield();
		logger.fine("Server started: "+this);

	}
	/**
	 * Stops the server; if the server is not running does nothing.
	 */
	public void stop() {
		if (!isAlive())
			return;
		isRunning = false;
		try {

//			InetAddress address=InetAddress.getLocalHost();
//			URL url=new URL("http",address.getHostName(),currentPort,"/");
//			url.getContent();

			String hostname=InetAddress.getLocalHost().getHostName();
			Socket teardownClient =
				new Socket(
					hostname,
					currentPort);
			OutputStream out = teardownClient.getOutputStream();
			logger.finest("Shutting down: "+this);
			out.write(("GET / http/1.0\nHost:blafasel\n\n\n").getBytes());
			out.flush();

			out.close();
			teardownClient.close();
			serverThread.join();
			logger.fine("Server down: "+this);
		} catch (Throwable t) {
			EventDispatcher.add(new ErrorEvent(t));
			logger.severe(t+"");
		}

	}
	/**
	 * @see java.lang.Runnable#run()
	 */
	public void run() {
		try {
			ServerSocket server = new ServerSocket(currentPort, 12);
			EventDispatcher.add(getServerStartEvent());
			logger.fine("Waiting for client accept..");
			do {
				// accept
				Socket client = server.accept();
				client.setReuseAddress(true);
				client.setKeepAlive(true);
				if (isRunning) {
					process(client);
				}

			} while (isRunning);
			server.close();
		} catch (Exception e) {
			EventDispatcher.add(new ErrorEvent(e));
		} finally {
			isRunning = false;
			EventDispatcher.add(
				new ApplicationEvent(
					ApplicationEvent.SERVER_STOPPED,
					"Server stopped."));
		}

	}
	/**
	 * @see java.lang.Object#toString()
	 */
	public String toString() {
		return getClass().getName()
			+ " port="
			+ getPort()
			+ " isRunning="
			+ isRunning;
	}
	/**
	 * Subclass this for working implementations.
	 * @param client
	 */
	protected abstract void process(Socket client);

	/**
	 * Subclasses must return the port actually
	 * configured
	 * @return
	 */
	protected abstract int getPort();

	/**
	 * Implementing classes should return an event indicating the
	 * Server Name and the LogTableColum headlines.
	 * @return
	 */
	protected abstract ApplicationEvent getServerStartEvent();

}
